﻿#ifndef __dGlove_h__
#define __dGlove_h__

#include <QObject>
#include <QPointer>
#include "Dg5Emulator/QtAsioThread.h"
#include "gloveCommands.h"
#include "GloveStructs.h"

class QXmlStreamWriter;
class QDomElement;

namespace vkbrd
{
	class GloveBuffer
	{
	public:
		/**
		* @return True if finished receiving new message.
		*/
		bool pushRecivedByte(unsigned char _byte);

		GloveBuffer(int _initialSize);

		gIntFingers& getAvgRead(){return avgGloveRead;} 

	private:
		bool fingersPressed[5];
		int incomingIndex;
		int bufferSize;
		int fingerReadsIndex;
		gIntFingers avgGloveRead;
		unsigned char incomingData[messageLenght];
		QList<gIntFingers> lastFingersReads;
		
		void recalculateAvarageRead();
		void processMessage();
	};

	struct FingerCalibration
	{
		MinMax minMax[fingerCount];

		void resetFinger(int _i)
		{
			minMax[_i].minPress = gloveMinValue;
			minMax[_i].maxPress = gloveMaxValue;
		}

		void reset()
		{
			for (int i=0;i<fingerCount;++i)
				resetFinger(i);
		}

		FingerCalibration()
		{ 
			reset();
		}

		void calibrateMinMax(BoolFingers _fingers, gIntFingers& _fingerReads);

		/**
		* Check new raw finger state against calibration data and prev state.
		* @return New message changed bool state of at leat one finger.
		*/
		bool updateBoolFingerState(BoolFingers& _prevState, const gIntFingers& _intFingers);

		void serialize( QXmlStreamWriter* _writer );

		void deserialize(QDomElement& _calibrationElement);
	};


	class DG5Glove: public QObject
	{
		Q_OBJECT

	public:
		DG5Glove ();
		~DG5Glove();

		/**
		* Send close message to glove ioThread, and block until its finished.
		*/
		void closeGloveComPort();

		bool openGloveComPort();

		void sendGloveCommand(unsigned char _command);

		bool connected();

		void setPortName(const QString& _name){portName = _name;}

		void serialize(QXmlStreamWriter* _writer);

		void deserialize(QDomElement& _gloveElement);

		/**
		*  Name of xml glove element to serialize/deserialize data.
		*/
		static const QString gloveXmlElementName;

		void calibrate(BoolFingers _fingers);

		/**
		* Set minPress to 0, and maxPress to glovePrecision on all fingers.
		*/
		void resetCalibration();

public slots:

		/**
		*  Message received from IOThred. The maximum message length is 512.
		* @param _msg Raw message recived.
		* @param _bytesCount Number of bytes received.
		*/
		void messageRecived(unsigned char *_msg, int _bytesCount);

signals:

		/**
		* Emitted to stop ioThred. Thread will be stopped sometime later, 
		* so use closeGloveComPort to block until its finished.
		*/
		void stopIOThread();

		/**
		* Emit this signal, to let ioThread send a byte of data.
		* This is used mainly for glove commands, so does not
		* need optimisation.
		*/
		void dispatchWriteToPort(unsigned char);

		/**
		* New raw data message recived. This signal contains
		* data after taking mean value from buffer. If buffer size
		* is zero - then this is pure raw data.
		* This signal
		* @param _gloveId GloveId.
		* @param _fingerData Raw finger data. (Mean value from buffer).
		*/
		void rawDataReceived(int _gloveId, gIntFingers _fingerData);

		/**
		* Emitted when calibration min/max value changes. 
		*/
		void calibrationChanged(int _gloveId, FingerCalibration _calibration);

		/**
		* Emitted when raw data after testing against calibration gives
		* different true/false value on at least one finger.
		*/
		void fingerStateChanged(int _gloveId, BoolFingers _fingerData);

	private:
		/**
		* Smart pointer to thread reading/writing to serial port.
		* Glove is not connected if this pointer is unset.
		*/
		QPointer<QtAsioThread>	ioThread;

		/**
		* Port name "COMX" on windows platform, /dev/xxx on linux.
		* @todo Add linux support
		*/
		QString portName;

		/**
		* @see GloveBuffer
		*/
		GloveBuffer buffer;

		//! Glove Id, usually 0 for left glove, and 1 for right glove.
		int gloveId;

		/**
		* @see FingerCalibration
		*/
		FingerCalibration fingerCalibration;

		/**
		*  Currernt finger state as true/false values.
		*/
		BoolFingers currentFingerState;

	public:
		void setId(int _id){gloveId = _id;}
		int getId(){return gloveId;}
		QString getPortName(){return portName;}
		FingerCalibration& getFingerCalibration(){return fingerCalibration;}
	};
} //namespace vkbrd

#endif //__dGlove_h__